package util.xmlmanager;

/*
 * Copyright (c) 2006 Sun Microsystems, Inc.  All rights reserved.  U.S.
 * Government Rights - Commercial software.  Government users are subject
 * to the Sun Microsystems, Inc. standard license agreement and
 * applicable provisions of the FAR and its supplements.  Use is subject
 * to license terms.
 *
 * This distribution may include materials developed by third parties.
 * Sun, Sun Microsystems, the Sun logo, Java and J2EE are trademarks
 * or registered trademarks of Sun Microsystems, Inc. in the U.S. and
 * other countries.
 *
 * Copyright (c) 2006 Sun Microsystems, Inc. Tous droits reserves.
 *
 * Droits du gouvernement americain, utilisateurs gouvernementaux - logiciel
 * commercial. Les utilisateurs gouvernementaux sont soumis au contrat de
 * licence standard de Sun Microsystems, Inc., ainsi qu'aux dispositions
 * en vigueur de la FAR (Federal Acquisition Regulations) et des
 * supplements a celles-ci.  Distribue par des licences qui en
 * restreignent l'utilisation.
 *
 * Cette distribution peut comprendre des composants developpes par des
 * tierces parties. Sun, Sun Microsystems, le logo Sun, Java et J2EE
 * sont des marques de fabrique ou des marques deposees de Sun
 * Microsystems, Inc. aux Etats-Unis et dans d'autres pays.
 */

import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Stack;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;


import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import util.xmlmanager.elements.Attribute;
import util.xmlmanager.elements.Element;
import util.xmlmanager.elements.ProcessingInstruction;
import util.xmlmanager.elements.XMLDocument;

public class XMLBuilder extends DefaultHandler {
	// Attributes:
	private StringBuffer textBuffer;
	private Element mainElement;
	private Stack<Element> element;

	// Constructor:
	public XMLBuilder() {}
	//Methods:
	public static XMLDocument getXMLRepresentation(String xmlFileName){
		XMLBuilder builder = new XMLBuilder();
		return builder.process(xmlFileName);
	}
	private XMLDocument process(String xmlFileName){
		// Use the default (non-validating) parser
		SAXParserFactory factory = SAXParserFactory.newInstance();

		// Initializing main element:
		this.textBuffer = null;
		this.mainElement = null;
		this.element = new Stack<Element>();

		try {
			// Parse the input
			SAXParser saxParser = factory.newSAXParser();
			saxParser.parse(new File(xmlFileName), this);
			
			return new XMLDocument(xmlFileName,this.mainElement);
		} catch (SAXParseException spe) {
			// Error generated by the parser
			System.out.println("\n** Parsing error" + ", line "
					+ spe.getLineNumber() + ", uri " + spe.getSystemId());
			System.out.println("   " + spe.getMessage());

			// Use the contained exception, if any
			Exception x = spe;

			if (spe.getException() != null) {
				x = spe.getException();
			}

			x.printStackTrace();
		} catch (SAXException sxe) {
			// Error generated by this application
			// (or a parser-initialization error)
			Exception x = sxe;

			if (sxe.getException() != null) {
				x = sxe.getException();
			}

			x.printStackTrace();
		} catch (ParserConfigurationException pce) {
			// Parser with specified options can't be built
			pce.printStackTrace();
		} catch (IOException ioe) {
			// I/O error
			ioe.printStackTrace();
		} catch (Throwable t) {
			t.printStackTrace();
		}
		return null;
	}

	// ===========================================================
	// SAX DocumentHandler methods
	// ===========================================================
	public void setDocumentLocator(Locator l) {
		// Save this to resolve relative URIs or to give diagnostics.
		System.out.println("LOCATOR");
		System.out.println("\n SYS ID: " + l.getSystemId());
	}

	public void startDocument() throws SAXException {
		System.out.println("START DOCUMENT");
		System.out.println("<?xml version='1.0' encoding='UTF-8'?>");
	}

	public void endDocument() throws SAXException {
		System.out.println("END DOCUMENT");
	}

	public void startElement(String namespaceURI, String sName, // simple name
			String qName, // qualified name
			Attributes attrs) throws SAXException {
		this.echoText();

		String eName = sName; // element name
		if ("".equals(eName)) {
			eName = qName; // not namespaceAware
		}

		LinkedList<Attribute> attributes = new LinkedList<Attribute>();

		if (attrs != null) {
			for (int i = 0; i < attrs.getLength(); i++) {
				String aName = attrs.getLocalName(i); // Attr name

				if ("".equals(aName)) {
					aName = attrs.getQName(i);
				}
				attributes.add(new Attribute(aName, attrs.getValue(i)));
			}
		}

		// Creating Structure:
		this.addElement(eName, attributes);
	}

	public void endElement(String namespaceURI, String sName, // simple name
			String qName // qualified name
	) throws SAXException {
		this.echoText();
		this.prepareReadyElement();
	}

	public void characters(char[] buf, int offset, int len) throws SAXException {
		String s = new String(buf, offset, len);

		if (textBuffer == null) {
			textBuffer = new StringBuffer(s);
		} else {
			textBuffer.append(s);
		}
	}

	public void ignorableWhitespace(char[] buf, int offset, int len)
			throws SAXException {
		// Ignore it
	}

	public void processingInstruction(String target, String data)
			throws SAXException {
		/*System.out.println("PROCESS: ");
		System.out.println("<?" + target + " " + data + "?>");*/
		this.addProcessingInstruction(target, data);
	}

	// ===========================================================
	// SAX ErrorHandler methods
	// ===========================================================
	public void error(SAXParseException e) throws SAXParseException {
		throw e;
	}

	// dump warnings too
	public void warning(SAXParseException err) throws SAXParseException {
		System.out.println("** Warning" + ", line " + err.getLineNumber()
				+ ", uri " + err.getSystemId());
		System.out.println("   " + err.getMessage());
	}

	private void echoText() throws SAXException {
		if (textBuffer == null) {
			return;
		}

		String s = "" + textBuffer;

		if (!s.trim().equals("")) {
			this.addData(s);
		}

		textBuffer = null;
	}

	// ===========================================================
	// Building Methods ...
	// ===========================================================

	private void addElement(String name, LinkedList<Attribute> attributes) {
		this.textBuffer = null;
		if (this.mainElement == null) {
			this.mainElement = new Element(name, attributes);
			this.element.push(this.mainElement);
		} else {
			this.element.push(new Element(name, attributes));
		}
	}

	private void addProcessingInstruction(String application, String instruction) {
		this.element.peek().addProcessingInstruction(new ProcessingInstruction(application,instruction));
	}

	private void addData(String data) {
		this.element.peek().setData(data);
	}

	private void prepareReadyElement() {
		Element tmp = this.element.pop();
		if (!this.element.isEmpty()) {
			this.element.peek().addElement(tmp);
		}
	}

	public String toString() {
		if (this.mainElement != null) {
			return this.mainElement.toString();
		}
		return null;
	}

	// Main:
	/**
	 * @param argv
	 */
	public static void main(String[] argv) {
		// Use an instance of ourselves as the SAX event handler
		XMLDocument document = XMLBuilder.getXMLRepresentation("slideSample06.xml");
		System.out.println("=================================");
		System.out.println("=================================");
		System.out.println("=================================");
		System.out.println(document.toString());
		System.out.println("================0================");
		System.exit(0);
		/*
		 * Stack<String>tmp = new Stack<String>(); tmp.push("a"); tmp.push("b");
		 * tmp.push("c"); tmp.push("d");
		 * System.out.println("Peack: "+tmp.peek()); for (String string : tmp) {
		 * System.out.println(string); }
		 */
	}
}
